//
// Created by Thinkpad on 2025/9/4.
//

#include "xlisttest.h"
#include <QDebug>
#include <common/xlist.hpp>

using namespace std;
using namespace xlist;

// test struct
struct simple_test
{
	int id;
	string name;

	auto tie() const
	{
		return std::make_tuple(id, name);
	}
};
Q_DECLARE_METATYPE(simple_test)
// 重载 << 操作符 (非成员函数)
ostream& operator<<(ostream& os, const simple_test& p)
{
	os << "simple{id: " << p.id
	   << ", name: " << p.name << "}";
	return os;// 必须返回 os，以支持链式调用 (如 cout << p1 << p2;)
}
// 重载 << 操作符 for QDebug
QDebug operator<<(QDebug debug, const simple_test& p)
{
	// 使用 noquote()/nospqce() 避免字符串被加上引号/去掉多余的空格
	debug.noquote().nospace() << "simple(" << p.id << ", " << p.name.c_str() << ")";
	return debug;// 同样必须返回 debug
}
// 重载 == 操作符 for QHash
bool operator==(const simple_test& a, const simple_test& b)
{
	return a.id == b.id && a.name == b.name;
}
// 提供 qHash 函数 for QHash
uint qHash(const simple_test& p, uint seed = 0)
{
	return qHash(QString::fromStdString(p.name), qHash(p.id, seed));
}
// 重载 < 操作符 for orderby
bool operator<(const simple_test& a, const simple_test& b)
{
	return a.id < b.id;
}
// 重载 > 操作符 for orderbydesc
bool operator>(const simple_test& a, const simple_test& b)
{
	return a.id > b.id;
}
// 字符串转大写
string to_upper(const string& input)
{
	string result;
	result.reserve(input.size());// 预分配空间，提高效率

	std::transform(input.begin(), input.end(), back_inserter(result),
				   [](unsigned char c) { return toupper(c); });

	return result;
}
//.
XListTest::XListTest(QObject* parent)
	: QObject(parent)
{
}

XListTest::~XListTest()
{
}

void XListTest::initTestCase()
{
	qDebug() << "CLASS:XListTest START";
}

void XListTest::tstXList()
{
	// where vs select
	{
		// where
		XList<int> list({1, 2, 3, 4, 5});
		list.Add(10);
		QCOMPARE(list.ToList().size(), 6);
		cout << "current all elements:" << endl;
		list.print();

		cout << "************* where operator *************" << endl;
		auto result = list.Where([](int n) { return n % 2 == 0; }).ToList();
		QVERIFY(!result.isEmpty());
		if (!result.isEmpty())
		{
			QCOMPARE(result.size(), 3);
			cout << "after where elements:" << endl;
			for (auto item: result)
				cout << "item --> " << item << endl;
		}
		auto result2 = list.Where([](int n) { return n % 2 != 0; });
		QCOMPARE(result2.ToList().size(), 3);
		result2.print();
		cout << "************* [s]original list *************" << endl;
		list.print();
		QCOMPARE(list.ToList().size(), 6);
		// select
		XList<simple_test> plist({{111, "t1"}, {222, "t2"}, {333, "t3"}});
		QCOMPARE(plist.ToList().size(), 3);
		cout << "[spt]current all elements:" << endl;
		plist.print();
		cout << "************* macro select operator *************" << endl;
		auto macList1 = plist.Select(SELECT_FIELDS(10 * s.id));
		macList1.print();
		auto macList2 = plist.Select(SELECT_FIELDS_OF(simple_test, 100 * s.id, to_upper(s.name)));
		macList2.printByQt();

		cout << "************* select + where operator *************" << endl;
		auto swList1 = plist.Select(SELECT_FIELDS(s.id, s.name))
							   .Where([](const tuple<int, string>& t) {
								   auto name = get<1>(t);
								   return 0 == name.compare("t2");
							   });
		swList1.print();
		cout << "where + select:" << endl;
		auto swList2 = plist.Where([](const simple_test& st) { return st.id != 222; })
							   .Select(SELECT_FIELDS(s.id, s.name));
		swList2.print();
		cout << "select all:" << endl;
		auto swList3 = plist.Select(SELECT_ALL());
		swList3.print();
	}
	// etc
	XList<simple_test> plist({{111, "t1"}, {222, "t2"}, {333, "t3"}});
	cout << "************* first operator *************" << endl;
	{
		auto first = plist.First();
		cout << "first element:" << first << endl;
		QCOMPARE(first.id, 111);
		auto pfirst = plist.First([](const simple_test& t) {
			return t.id == 222;
		});
		QCOMPARE(pfirst.id, 222);
		cout << "first(p) element:" << pfirst << endl;
	}
	cout << "************* firstordefault operator *************" << endl;
	{
		auto dfirst = plist.FirstOrDefault();
		QCOMPARE(dfirst.id, 111);
		cout << "first(d) element:" << dfirst << endl;

		auto pdfirst = plist.FirstOrDefault([](const simple_test& t) {
			return t.id == 222;
		});
		QCOMPARE(pdfirst.id, 222);
		cout << "dfirst(pd) element:" << pdfirst << endl;
	}
	cout << "************* single operator *************" << endl;
	// 与 singleordefault 二选一，因为异常输出会影响打印效果，功能不影响
	{
		//        auto single = plist.Single();
		//        cout << "single element:" << single << endl;

		//        auto psingle = plist.Single([](const simple_test& t){
		//            return t.name == "t2";
		//        });
		//        cout << "single(p) element:" << psingle << endl;
	}
	cout << "************* singleordefault operator *************" << endl;
	{
		auto dsingle = plist.SingleOrDefault();
		QCOMPARE(dsingle.id, 0);
		cout << "single(d) element:" << dsingle << endl;

		auto pdsingle = plist.SingleOrDefault([](const simple_test& t) {
			return t.id == 444;
		});
		QCOMPARE(pdsingle.id, 0);
		cout << "single(pd) element:" << pdsingle << endl;
	}
	cout << "************* last operator *************" << endl;
	{
		auto last = plist.Last();
		QCOMPARE(last.id, 333);
		cout << "last element:" << last << endl;
	}
	cout << "************* lastordefault operator *************" << endl;
	{
		auto dlast = plist.LastOrDefault();
		QCOMPARE(dlast.id, 333);
		cout << "last(d) element:" << dlast << endl;
	}
	cout << "************* any operator *************" << endl;
	{
		bool any = plist.Any();
		QVERIFY(any);
		cout << "has any element:" << (any ? "true" : "false") << endl;

		any = plist.Any([](const simple_test& t) {
			return t.id > 333;
		});
		QVERIFY(!any);
		cout << "has any(p) element:" << (any ? "true" : "false") << endl;
	}
	cout << "************* all operator *************" << endl;
	{
		bool all = plist.All([](const simple_test& t) {
			return t.id >= 111;
		});
		QVERIFY(all);
		cout << "all element has ok:" << (all ? "true" : "false") << endl;
	}
	cout << "************* contains operator *************" << endl;
	{
		simple_test st{333, "t3"};
		bool contains = plist.Contains(st);
		QVERIFY(contains);
		cout << "contains element:" << (contains ? 1 : 0) << endl;
	}
	cout << "************* distinct operator *************" << endl;
	{
		XList<simple_test> plist1({{111, "t1"}, {222, "t2"}, {333, "t3"}, {222, "t2"}});
		auto distinct = plist1.Distinct();
		QCOMPARE(distinct.ToList().size(), 3);
		cout << "distinct elements:" << endl;
		distinct.print();

		auto pdistinct = plist1.Distinct([](const simple_test& t) {
			return t.id;
		});
		QCOMPARE(pdistinct.ToList().size(), 3);
		cout << "distinct(p) elements:" << endl;
		pdistinct.print();
	}
	cout << "************* orderby operator *************" << endl;
	{
		XList<simple_test> plist2({{222, "t2"}, {111, "t1"}, {333, "t3"}});
		auto orderby = plist2.OrderBy();
		cout << "orderby elements:" << endl;
		orderby.print();

		auto porderby = plist2.OrderBy([](const simple_test& s, const simple_test& t) {
			return s.id < t.id;
		});
		cout << "orderby(p) elements:" << endl;
		porderby.print();
	}
	cout << "************* orderbydescending operator *************" << endl;
	{
		auto desc = plist.OrderByDescending();
		cout << "orderbydescending elements:" << endl;
		desc.print();

		auto pdesc = plist.OrderByDescending([](const simple_test& s, const simple_test& t) {
			return s.id > t.id;
		});
		cout << "orderbydescending(p) elements:" << endl;
		pdesc.print();
	}
	cout << "************* concat operator *************" << endl;
	{
		XList<simple_test> p1({{111, "t1"}, {222, "t2"}});
		XList<simple_test> p2({{222, "t2"}, {333, "t3"}});

		auto concat = p1.Concat(p2);
		QCOMPARE(concat.ToList().size(), 4);
		cout << "concat elements:" << endl;
		concat.print();
	}
	cout << "************* union operator *************" << endl;
	{
		XList<simple_test> p1({{111, "t1"}, {222, "t2"}});
		XList<simple_test> p2({{222, "t2"}, {333, "t3"}});

		auto unionlist = p1.Union(p2);
		QCOMPARE(unionlist.ToList().size(), 3);
		cout << "union elements:" << endl;
		unionlist.print();
	}
	cout << "************* groupby operator *************" << endl;
	{
		XList<simple_test> plist3({{111, "t1"}, {222, "t2-0"}, {333, "t3"}, {222, "t2-1"}});
		auto grouped = plist3.GroupBy([](const simple_test& t) {
			return t.id;
		});
		QVERIFY(!grouped.isEmpty());
		if (!grouped.isEmpty())
		{
			QCOMPARE(grouped.size(), 3);
			for (const auto& pair: grouped)
			{
				auto key = pair.first;
				auto val = pair.second;
				cout << "group :" << key << endl;
				if (!val.isEmpty())
				{
					for (const auto& item: val)
						cout << "element --> " << item << endl;
				}
			}
		}
	}
	cout << "************* todictionary operator *************" << endl;
	{
		auto dictionary = plist.ToDictionary([](const simple_test& t) {
			return t.id;
		});
		QVERIFY(!dictionary.isEmpty());
		if (!dictionary.isEmpty())
		{
			QCOMPARE(dictionary.size(), 3);
			cout << "dictionary(by key) elements:" << endl;
			for (auto iter = dictionary.begin(); iter != dictionary.end(); ++iter)
			{
				cout << "key->" << iter.key() << endl;
				cout << "val->" << iter.value() << endl;
			}
		}

		auto pdictionary = plist.ToDictionary(
				[](const simple_test& k) { return k.id; },
				[](const simple_test& v) { return v.name; });
		QVERIFY(!pdictionary.isEmpty());
		if (!pdictionary.isEmpty())
		{
			QCOMPARE(pdictionary.size(), 3);
			cout << "dictionary(k->v) elements:" << endl;
			for (auto iter = pdictionary.begin(); iter != pdictionary.end(); ++iter)
			{
				cout << "key->" << iter.key() << endl;
				cout << "val->" << iter.value() << endl;
			}
		}
	}

	cout << "************* [e]original list *************" << endl;
	QCOMPARE(plist.ToList().size(), 3);
	plist.print();
}
void XListTest::cleanupTestCase()
{
	qDebug() << "CLASS:XListTest END";
}

// QTEST_MAIN(XListTest)
int main(int argc, char** argv) {
	XListTest tc;
	return QTest::qExec(&tc, argc, argv); // qExec 会把失败信息打印到 stdout
}
